package com.zzyy.study.service.impl;

import com.zzyy.study.entities.Account;
import com.zzyy.study.entities.AccountFreeze;
import com.zzyy.study.mapper.AccountFreezeMapper;
import com.zzyy.study.mapper.AccountMapper;
import com.zzyy.study.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

/**
 * @auther zzyy
 * @create 2023-12-08 21:49
 */
@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService
{
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private AccountFreezeMapper accountFreezeMapper;


    @Override
    @Transactional
    public void decreaseTcc(Long userId, Long money)
    {
        String xid = RootContext.getXID();
        //1 业务悬挂判断，避免空悬挂，已经执行过回滚了就不能再try预留资源
        // tcc_storage_freeze表中是否有冻结记录，等于null正确，如果不等于null一定是CANCEL执行过，拒绝业务悬挂
        AccountFreeze accountFreezeFromDB = accountFreezeMapper.selectByPrimaryKey(xid);
        if (accountFreezeFromDB != null) {
            return ; //CANCEL执行过，拒绝业务往下走，多出来一个孤魂野鬼try
        }
        //2 dtx_account账户余额判断
        Example wherCondition=new Example(Account.class);
        Example.Criteria criteria=wherCondition.createCriteria();
        criteria.andEqualTo("userId",userId);
        Account account = accountMapper.selectOneByExample(wherCondition);

        if(money > account.getTotalMoney())  throw new RuntimeException("账户余额不足，预占资源失败");

        //3 dtx_account账户余额冻减
        accountMapper.decreaseTcc(userId,money);
        //4 dtx_account_freeze  tcc冻结情况
        AccountFreeze accountFreeze = new AccountFreeze();
        accountFreeze.setXid(RootContext.getXID());
        accountFreeze.setUserId(userId+"");
        accountFreeze.setFreezeMoney(Math.toIntExact(money));
        accountFreeze.setTccStatus(AccountFreeze.TccStatusEnum.TRY);

        accountFreezeMapper.insertSelective(accountFreeze);
    }

    @Override
    @Transactional
    public Boolean confirm(BusinessActionContext ctx)
    {
        System.out.println("--------come in confirm ctx.getXid(): " + ctx.getXid());
        //1 幂等性判断，delete操作删除1次和100次相同，天生幂等性

        //2 修改订单状态，从try→confirm或者你不想保留直接删除t_storage_freeze里面的TRY状态数据
        int i = accountFreezeMapper.deleteByPrimaryKey(ctx.getXid());

        return i == 1;
    }

    @Override
    @Transactional
    public Boolean cancel(BusinessActionContext ctx)
    {
        System.out.println("--------come in cancel ctx.getXid(): " + ctx.getXid());

        //1 dtx_account_freeze表中按照xid查冻结账户
        AccountFreeze accountFreeze = accountFreezeMapper.selectByPrimaryKey(ctx.getXid());
        //2 空回滚判断,允许空回滚存在，冻结账户为null说明try没有执行，需要空回滚处理
        if (accountFreeze == null)
        {
            //说明try没有执行，需要空回滚处理
            AccountFreeze accountFreezeNullRollBack = new AccountFreeze();

            accountFreezeNullRollBack.setXid(ctx.getXid());
            accountFreezeNullRollBack.setUserId(ctx.getActionContext("userId").toString());
            accountFreezeNullRollBack.setFreezeMoney(0);
            accountFreezeNullRollBack.setTccStatus(AccountFreeze.TccStatusEnum.CANCEL);

            accountFreezeMapper.insertSelective(accountFreezeNullRollBack);
            return true;
        }
        //3 判断幂等性
        if (accountFreeze.getTccStatus() == AccountFreeze.TccStatusEnum.CANCEL) {
            return true;//已经Cancel过了，不要重复处理了,保证幂等性
        }

        //4 回退账户余额冻减
        Long money = Long.valueOf(accountFreeze.getFreezeMoney());
        Long userId = Long.valueOf(accountFreeze.getUserId());
        accountMapper.undecreaseTcc(userId,money);
        //5 账户金额清零并修改状态为CANCEL
        accountFreeze.setFreezeMoney(0);
        accountFreeze.setTccStatus(AccountFreeze.TccStatusEnum.CANCEL);
        int i = accountFreezeMapper.updateByPrimaryKey(accountFreeze);

        return i == 1;
    }
}
